﻿using System;
using System.Collections;


namespace Chess_Model
{


    public class ChessBoard
    {

        // Legal range constants are between [0, X_RANGE-1] and [0, Y_RANGE-1] inclusive
        public const int X_RANGE = 8;
        public const int Y_RANGE = 8;

        // Grid[x][y] is set to instance of Piece subclass, null otherwise	
        private Piece[,] pieces; //valid syntax

        //use to hold the position of the kings
        private Piece whiteKing;
        private Piece blackKing;


        // Initialize and set up board to initial state
        public ChessBoard()
        {
            pieces = new Piece[X_RANGE, Y_RANGE];

            //INIT default position for all pieces
            //WHITE PLAYER
                //Pawns
                for (int i = 0; i < X_RANGE; i++)
                {
                    Pawn pawn    = new Pawn(new Position(i, 6), Piece.WHITE);
                    pieces[i, 6] = pawn;
                }
                //Knights
                pieces[1, 7] = new Knight(new Position(1, 7), Piece.WHITE);
                pieces[6, 7] = new Knight(new Position(6, 7), Piece.WHITE);
                //Rooks
                pieces[0, 7] = new Rook(new Position(0, 7), Piece.WHITE);
                pieces[7, 7] = new Rook(new Position(7, 7), Piece.WHITE);
                //Bishops
                pieces[2, 7] = new Bishop(new Position(2, 7), Piece.WHITE);
                pieces[5, 7] = new Bishop(new Position(6, 7), Piece.WHITE);
                //Queen and King
                pieces[3, 7] = new Queen(new Position(3, 7), Piece.WHITE);
                //pieces[4, 7] = new King(new Position(4, 7), Piece.WHITE);
                whiteKing = new King(new Position(4, 7), Piece.WHITE);
                pieces[4, 7] = whiteKing;

            //BLACK PLAYER
                //Pawns
                for (int i = 0; i < X_RANGE; i++)
                {
                    Pawn pawn    = new Pawn(new Position(i, 1), Piece.BLACK);
                    pieces[i, 1] = pawn;
                }
                //Knights
                pieces[1, 0] = new Knight(new Position(1, 0), Piece.BLACK);
                pieces[6, 0] = new Knight(new Position(6, 0), Piece.BLACK);
                //Rooks
                pieces[0, 0] = new Rook(new Position(0, 0), Piece.BLACK);
                pieces[7, 0] = new Rook(new Position(7, 0), Piece.BLACK);
                //Bishops
                pieces[2, 0] = new Bishop(new Position(2, 0), Piece.BLACK);
                pieces[5, 0] = new Bishop(new Position(5, 0), Piece.BLACK);
                //Queen and King
                pieces[3, 0] = new Queen(new Position(3, 0), Piece.BLACK);
                //pieces[4, 0] = new King(new Position(4, 0), Piece.BLACK);
                blackKing = new King(new Position(4, 0), Piece.BLACK);
                pieces[4, 0] = blackKing;

        }


        // Move a piece from old position to new position
        // Return true if the move is successful, false otherwise.
        public bool move(Position oldPos, Position newPos)
        {
            
            if (ChessUtilities.isInRange(oldPos) && ChessUtilities.isInRange(newPos))
            {
                Piece currentpiece = getPiece(oldPos);
                if (!isValidMove(currentpiece, newPos))
                {
                    //throw illegal moves exception
                    //Console.WriteLine("Illegal Move");
                    // XXX: Added to return false here since it is not valid.
                    //      Preferably to just return without printing error
                    //      since the CommandHandler is the user interface (the
                    //      View in our MVC). - vhc
                    return false;
                }
                else
                {
                    pieces[oldPos.x, oldPos.y] = null;
                    currentpiece.moveTo(newPos);
                    pieces[newPos.x, newPos.y] = currentpiece;
                    
                    return true;
                }
                // return true;
            }
            else
            {
                //OR maybe throw exceptions?
                return false;
            }
        }

        // Overloaded for convenience. 
        // Caller won't need to create Position instances explicitly.
        public bool move(int x1, int y1, int x2, int y2)
        {
            return move(new Position(x1, y1), new Position(x2, y2));
        }


        // Compares the piece's legal moves with the board
        // and returns a list of positions that a piece can
        // move to
        //  
        // NOTE: THIS IS A BIG ASS METHOD. Consider revising the code
        private ArrayList checkLegalMoves(Piece p)
        {
            // Generate piece's all legal moves
            ArrayList legalmoves = p.getNextLegalMoves();

            // Convert the arraylist to array to avoid stupid list modified exception
            Position[] legalmovesarray = (Position[])legalmoves.ToArray(typeof(Position));

            // Get the piece's current position
            Position currentposition = p.getCurrentPosition();

            // Loop over the array to find 
            // a piece that's in the way
            for(int i=0;i<legalmovesarray.Length;i++)
            {
                Position pos = legalmovesarray[i];
                Piece boardpiece = null;
                if (pos != null)
                {
                    boardpiece = getPiece(pos);
                }
                
                // look for a taken position
                if (boardpiece != null)
                {
                    // if a piece is on the same team
                    // remove that position from the list
                    // also remove other positions that is behind this piece
                   
                    
                        // Get the offset distance between 2 vectors 
                        int dx = pos.x - currentposition.x;
                        int dy = pos.y - currentposition.y;
                        // Get the "1" (step) by dividing to its 
                        // absolute value so we can keep its sign
                        int xstep = 0;
                        int ystep = 0;
                        if (dx != 0)
                        {
                            xstep = dx / Math.Abs(dx);
                        }
                        if (dy != 0)
                        {
                            ystep = dy / Math.Abs(dy);
                        }

                        // Add the step to the our pos so we
                        // can get the position behind it
                        int xbehind = pos.x + xstep;
                        int ybehind = pos.y + ystep;

                        // Create the invalid position 
                        Position posbehind = new Position(xbehind, ybehind);

                        // Keep adding steps to the position
                        // while removing it from the array
                        while (ChessUtilities.isInRange(posbehind))
                        {
                            for (int j = 0; j < legalmovesarray.Length; j++)
                            {
                                Position posit = legalmovesarray[j];
                                if (posit != null)
                                {
                                    if (posit.Equals(posbehind))
                                    {

                                        legalmovesarray[j] = null;
                                    }
                                }
                            }
                            posbehind = new Position(posbehind.x + xstep, posbehind.y + ystep);
                        }

                        // Also remove the position of the piece
                        // which is on the same team of current piece
                        if (boardpiece.isSameTeam(p))
                            legalmovesarray[i] = null;
                    
                 
                }
            }

            // Add everything else in the array to the arraylist
            // and return it
            legalmoves = new ArrayList();
            foreach (Position a in legalmovesarray)
            {
                if (a != null)
                {
                    legalmoves.Add(a);
                }
            }
            return legalmoves;
        }

        // Return true if the new position is valid for given piece
        // assumming that newPos is a valid position.
        public bool isValidMove(Piece p, Position newPos)
        {
            bool result = false;
            ArrayList legalmoves = checkLegalMoves(p);
           
            foreach (Position pos in legalmoves)
            {
                
                if (pos.Equals(newPos))
                {
                    result = true;             
                }
            }
            return result;
        }


        // Print text representation of board and pieces
        public void printBoard()
        {
           for (int y = 0; y < Y_RANGE; y++)
            {
                for (int x = 0; x < X_RANGE; x++)
                {
                    if (pieces[x, y] != null)
                    {
                        Console.Write(pieces[x, y].toString());
                        Console.Write(" ");
                    }
                    else
                    {
                        Console.Write("0  ");
                    }
                }
                Console.WriteLine();
            }
           Console.WriteLine();

        }


        // Return true if a player is checking the other
        public bool isCheck()
        {
            //array use to take in each pieces on board available moves
            ArrayList piecesmoves;

            //get the position of the kings
            Position black = blackKing.getCurrentPosition();
            Position white = whiteKing.getCurrentPosition();

            //loop through all the pieces
            for (int x = 0; x < X_RANGE; x++)
            {
                for (int y = 0; y < Y_RANGE; y++)
                {

                    //if there's a piece than get the list and see if it's in check
                    if (pieces[x, y] != null)
                    {
                        piecesmoves = checkLegalMoves(pieces[x, y]);
                        foreach (Position a in piecesmoves)
                        {

                            if (a.x == black.x && a.y == black.y)
                            {
                                Console.WriteLine("Black in check");
                                if(isCheckmate(blackKing))
                                    Console.WriteLine("Checkmate Black");
                                return true;
                            }
                            else if (a.x == white.x && a.y == white.y)
                            {
                                Console.WriteLine("White in check");
                                if(isCheckmate(whiteKing))
                                    Console.WriteLine("Checkmate White");
                                return true;
                            }
                        }

                    }
                }
            }
            return false;
        }


        // Return true if a player is checkmating the other	
        public bool isCheckmate(Piece king)
        {
            //this is used to hold the list of the other pieces moves
            ArrayList piecesmoves;
            //holds the legal moves of the king
            ArrayList legalmoves = checkLegalMoves(king);
            
            //change the arraylist into array of position
            Position[] legalmovesarray = (Position[])legalmoves.ToArray(typeof(Position));

            //use to hold how many moves the king is limited to
            int checkmate = 0;

            //for every move the king has check if any piece can potentially taken up that place
            //if the space can be takened up then the king move is limited there, so we increment the checkmate integer
            //but if that space can be occupied by one of the kings other pieces then it is not checkmate so we return false.
            for (int i = 0; i < legalmovesarray.Length; i++)
            {
                for (int x = 0; x < X_RANGE; x++)
                {
                    for (int y = 0; y < Y_RANGE; y++)
                    {
                        if (pieces[x, y] != null && !king.isSameTeam(pieces[x, y]))
                        {
                            piecesmoves = checkLegalMoves(pieces[x, y]);
                            foreach (Position a in piecesmoves)
                            {
                                if (a.Equals(legalmovesarray[i]))
                                    checkmate++;
                            }
                            if (checkmate == legalmovesarray.Length)
                                return true;
                        }
                        else if (pieces[x, y] != null)
                        {
                            piecesmoves = checkLegalMoves(pieces[x, y]);
                            foreach (Position a in piecesmoves)
                            {
                                if (a.Equals(legalmovesarray[i]))
                                    return false;
                            }

                        }
                    }
                }
            }
            return false;
        }


        // Return a piece located at given position if valid, null otherwise
        // assumming that position is a valid position
        private Piece getPiece(Position pos)
        {            
            return pieces[pos.x,pos.y];
        }


        // Return true if the given position is empty, null otherwise or on error
        private bool isEmpty(Position pos)
        {
            return getPiece(pos) == null;
        }


    }

}
